//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#include <buola/python.h>

#include <buola/python/types.h>
#include <buola/python/ctype.h>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"

namespace buola { namespace python {

std::unordered_map<PyTypeObject*,CType*> CType::mAllTypes;

CType::CType(const std::string &pModuleName,const std::string &pName,TPythonNewObject pNewObject,CType *pInherited)
    :   CMethodPool(pInherited)
    ,   CMemberPool(pInherited)
    ,   mModuleName(pModuleName)
    ,   mName(pName)
    ,   mDoc(pName)
    ,   mNewObject(pNewObject)
{
    mNewObject=pNewObject;

    PyTypeObject lType=
    {
        PyVarObject_HEAD_INIT(nullptr,0)
        nullptr,                   //* //tp_name
        0,                      //* //tp_basicsize
        0,                          //tp_itemsize
        nullptr,                   //* //tp_dealloc
        0,                          //tp_print
        0,                          //tp_getattr
        0,                          //tp_setattr
        0,                          //tp_reserved
        nullptr,                   //* //tp_repr
        nullptr,                   //* //tp_as_number
        nullptr,                   //* //tp_as_sequence
        0,                          //tp_as_mapping
        0,                          //tp_hash
        nullptr,                   //* //tp_call
        nullptr,                   //* //tp_str
        0,                          //tp_getattro
        0,                          //tp_setattro
        0,                          //tp_as_buffer
        Py_TPFLAGS_DEFAULT,     //* //tp_flags
        nullptr,                   //* //tp_doc
        0,                          //tp_traverse
        0,                          //tp_clear
        nullptr,                   //* //tp_richcompare
        0,                          //tp_weaklistoffset
        0,                          //tp_iter
        0,                          //tp_iternext
        nullptr,                   //* //tp_methods
        nullptr,                   //* //tp_members
        nullptr,                   //* //tp_getset
        0,                          //tp_base
        0,                          //tp_dict
        0,                          //tp_descr_get
        0,                          //tp_descr_set
        0,                          //tp_dictoffset
        nullptr,                   //* //tp_init
        0,                          //tp_alloc
        nullptr                    //* //tp_new
    };

    mType=new PyTypeObject(lType);

    if(pInherited)
    {
        mType->tp_str=pInherited->mType->tp_str;
        mType->tp_repr=pInherited->mType->tp_repr;
        mType->tp_base=pInherited->mType;
        if(pInherited->mType->tp_as_number)
        {
            mType->tp_as_number=alloc_array_z<PyNumberMethods>();
            *mType->tp_as_number=*pInherited->mType->tp_as_number;
        }
        if(pInherited->mType->tp_as_sequence)
        {
            mType->tp_as_sequence=alloc_array_z<PySequenceMethods>();
            *mType->tp_as_sequence=*pInherited->mType->tp_as_sequence;
        }
    }
}

void CType::Init()
{
    mType->tp_dealloc=(destructor)CObjectBase::PythonDeAlloc;
    mType->tp_init=(initproc)CObjectBase::PythonInit;
    mType->tp_new=(newfunc)CObjectBase::PythonNew;
    mType->tp_basicsize=CObjectBase::GetPythonObjectSize();
    mType->tp_name=strdup((mModuleName+'.'+mName).c_str());
    mType->tp_doc=strdup(mDoc.c_str());
    mType->tp_methods=mMethods;
    mType->tp_getset=mGetSets;

    if(PyType_Ready(mType)<0)
        throw XInternal("can't init Python type");

    mAllTypes[mType]=this;
}

void CType::SetStrFunc(reprfunc pFunc)
{
    mType->tp_str=pFunc;
}

void CType::SetReprFunc(reprfunc pFunc)
{
    mType->tp_repr=pFunc;
}

void CType::SetCompFunc(richcmpfunc pFunc)
{
    mType->tp_richcompare=pFunc;
}

void CType::SetLenFunc(lenfunc pFunc)
{
    if(!mType->tp_as_sequence)
    {
        mType->tp_as_sequence=alloc_array_z<PySequenceMethods>();
    }

    mType->tp_as_sequence->sq_length=pFunc;
}

void CType::SetIndexFunc(ssizeargfunc pFunc)
{
    if(!mType->tp_as_sequence)
    {
        mType->tp_as_sequence=alloc_array_z<PySequenceMethods>();
    }

    mType->tp_as_sequence->sq_item=pFunc;
}

void CType::SetHashFunc(hashfunc pFunc)
{
    mType->tp_hash=pFunc;
}

void CType::SetGetAttrFunc(getattrofunc pFunc)
{
    mType->tp_getattro=pFunc;
}

void CType::SetSetAttrFunc(setattrofunc pFunc)
{
    mType->tp_setattro=pFunc;
}

void CType::SetDirFunc(PyCFunction pFunc)
{
    AddMethod("__dir__",pFunc,METH_NOARGS,L"lists methods and variables");
}

void CType::SetCallFunc(ternaryfunc pFunc)
{
    mType->tp_call=pFunc;
}

void CType::AddOperator(int pOp,unaryfunc pFunction)
{
    if(!mType->tp_as_number)
    {
        mType->tp_as_number=alloc_array_z<PyNumberMethods>();
    }

    switch(pOp)
    {
    case PYTHON_OP_NEG:
        mType->tp_as_number->nb_negative=(unaryfunc)pFunction;
        break;
    }
}

void CType::AddOperator(int pOp,binaryfunc pFunction)
{
    if(!mType->tp_as_number)
    {
        mType->tp_as_number=alloc_array_z<PyNumberMethods>();
    }

    switch(pOp)
    {
    case PYTHON_OP_ADD:
        mType->tp_as_number->nb_add=(binaryfunc)pFunction;
        break;
    case PYTHON_OP_SUB:
        mType->tp_as_number->nb_subtract=(binaryfunc)pFunction;
        break;
    case PYTHON_OP_MUL:
        mType->tp_as_number->nb_multiply=(binaryfunc)pFunction;
        break;
    case PYTHON_OP_DIV:
        mType->tp_as_number->nb_true_divide=(binaryfunc)pFunction;
        break;
    }
}

void CType::AddOperator(int pOp,ternaryfunc pFunction)
{
    if(!mType->tp_as_number)
    {
        mType->tp_as_number=alloc_array_z<PyNumberMethods>();
    }

    switch(pOp)
    {
    case PYTHON_OP_POW:
        mType->tp_as_number->nb_power=(ternaryfunc)pFunction;
    }
}

CObjectBase *CType::GetNew(PyTypeObject *pType,PyObject *pArgs)
{
    CType *lType=mAllTypes[pType];

    return lType->mNewObject(pArgs);
}

/*namespace python*/ } /*namespace buola*/ }

#pragma GCC diagnostic pop
